package com.glsx.auto;

import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.io.CharStreams;
import com.google.common.io.Files;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Exclusion;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;

import java.io.*;
import java.util.List;
import java.util.Properties;

import static com.glsx.auto.Constant.*;

@Mojo(name = "auto-skeleton",
        aggregator = true, // --non-recursive
        defaultPhase = LifecyclePhase.INITIALIZE
)
public class AutoSkeletonMojo extends AbstractAutoMojo {

    private Model createModel(String artifactId, String version, String packaging) {
        Model model = new Model();
        model.setGroupId(mavenProject.getGroupId());
        model.setArtifactId(artifactId);
        model.setVersion(version);
        model.setPackaging(packaging);
        model.setModelVersion(mavenProject.getModelVersion());
        return model;
    }

    private void createCommonPomXml() {
        File file = new File(common.getModuleDirectory()+"/pom.xml");
        if(file.exists()) {
           return;
        }
        MavenXpp3Writer mavenXpp3Writer = new MavenXpp3Writer();
        Model model = createModel(common.getModuleName(), "1.0.0-SNAPSHOT", "war");
        try {
            mavenXpp3Writer.write(new FileOutputStream(file), model);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Dependency createDependecy(String groupId, String artifactId, String version) {
        Dependency dependency = new Dependency();
        dependency.setVersion(version);
        dependency.setGroupId(groupId);
        dependency.setArtifactId(artifactId);
        return dependency;
    }

    public Exclusion createExclusion(String groupId, String artifactId) {
        Exclusion exclusion = new Exclusion();
        exclusion.setGroupId(groupId);
        exclusion.setArtifactId(artifactId);

        return exclusion;
    }


    public Dependency createDubboDependecy() {
        Dependency dependency = createDependecy("com.alibaba", "dubbo", "2.5.3");
        dependency.setExclusions(Lists.newArrayList(createExclusion("org.springframework", "spring")));
        return dependency;
    }


    public Dependency createZookeeperDependecy() {
        Dependency dependency = createDependecy("org.apache.zookeeper", "zookeeper", "3.3.3");

        dependency.setExclusions(Lists.newArrayList(
                createExclusion("com.sun.jmx", "jmxri"),
                createExclusion("com.sun.jdmk", "jmxtools"),
                createExclusion("javax.jms", "jms")
        ));

        return dependency;
    }

    public Dependency createZkclientDependecy() {
        return createDependecy("com.github.sgroschupf", "zkclient", "0.1");
    }

    private void createContainerPomXml() {
        File file = new File(container.getModuleDirectory()+"/pom.xml");
        if(file.exists()) {
            return;
        }
        MavenXpp3Writer mavenXpp3Writer = new MavenXpp3Writer();
        Model model = createModel(container.getModuleName(), "1.0.0-SNAPSHOT", "war");

        Properties properties = new Properties();
        properties.setProperty("org.springframework.version", springframeworkVersion);
        properties.setProperty("org.hibernate.version", hibernateVersion);
        model.setProperties(properties);

        model.setDependencies(Lists.newArrayList(
                createDubboDependecy(),
                createZookeeperDependecy(),
                createZkclientDependecy(),
                createDependecy(mavenProject.getGroupId(), common.getModuleName(), "1.0.0-SNAPSHOT"),
                createDependecy("org.hibernate", "hibernate-core", "${org.hibernate.version}"),
                createDependecy("org.springframework", "spring-core", "${org.springframework.version}"),
                createDependecy("org.springframework", "spring-aop", "${org.springframework.version}"),
                createDependecy("org.springframework", "spring-context", "${org.springframework.version}"),
                createDependecy("org.springframework", "spring-context-support", "${org.springframework.version}"),
                createDependecy("org.springframework", "spring-expression", "${org.springframework.version}"),
                createDependecy("org.springframework", "spring-beans", "${org.springframework.version}")));

        try {

            mavenXpp3Writer.write(new FileOutputStream(file), model);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void addModules(List<String> modules) {
        MavenXpp3Reader mavenXpp3Reader = new MavenXpp3Reader();
        File file = new File(mavenProject.getBasedir() + "/pom.xml");
        try {
            Model model = mavenXpp3Reader.read(new FileInputStream(file));
            modules.removeAll(model.getModules());

            if(!modules.isEmpty()) {
                model.setModules(modules);
                model.setPackaging("pom");

                MavenXpp3Writer mavenXpp3Writer = new MavenXpp3Writer();
                mavenXpp3Writer.write(new FileOutputStream(file), model);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        }
    }

    private void copyPropertyFile(String fileName) {
        String toFile = container.getResourceDirectory()+File.pathSeparatorChar+fileName;
        File file = new File(toFile);
        if(file.exists()) {
            return;
        }
        try {
            String content = Utils.readResource(fileName)
                    .replace(PROJECT_NAME_PLACEHOLDER, container.getModuleName());
            Files.write(content.getBytes(), file);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void copyConfigFile(String fileName) {
        String toFile = container.getResourceDirectory()+File.pathSeparatorChar+fileName;
        File file = new File(toFile);
        if(file.exists()) {
            return;
        }
        try {
            String content = Utils.readResource(fileName)
                    .replace(ROOT_PACKAGE_PLACEHOLDER, container.getTopLevelPackageName())
                    .replace(SERVICE_IMPL_PACKAGE_PLACEHOLDER, container.getPackageName(DefaultPackage.CONTAINER_SERVICE_IMPL));
            Files.write(content.getBytes(), file);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void copyBinShellFile(String fileName) {
        String toFile = container.getWebInfDirectory()+File.pathSeparatorChar+fileName;
        File file = new File(toFile);
        if(file.exists()) {
            return;
        }
        try {
            CharStreams.copy(new InputStreamReader(Utils.getResourceAsStream(fileName), Charsets.UTF_8),
                    new FileWriter(toFile));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void copyBinShellTemplates() {
        copyBinShellFile(DUMP_TEMPLATE);
        copyBinShellFile(RESTART_TEMPLATE);
        copyBinShellFile(SERVER_TEMPLATE);
        copyBinShellFile(START_TEMPLATE);
        copyBinShellFile(STOP_TEMPLATE);
    }

    private void copyPropertyTemplates() {
        copyPropertyFile(LOG4J_TEMPLATE);
        copyPropertyFile(SYSTEM_TEMPLATE);
    }

    private void copyConfigTemplates() {
        copyConfigFile(DUBBO_PROVIDER_TEMPLATE);
        copyConfigFile(DUBBO_CONSUMER_TEMPLATE);
        copyConfigFile(DATASOURCE_TEMPLATE);
        copyConfigFile(CONTEXT_TEMPLATE);
    }

    private void copyTemplates() {
        copyBinShellTemplates();
        copyConfigTemplates();
        copyPropertyTemplates();
    }

    @Override
    public void process() {

        if(new File(common.getModuleDirectory()).exists()) {
            return;
        }

        if(new File(container.getModuleDirectory()).exists()) {
            return;
        }

        if(!createCommonDirectories()) {
            return;
        }
        if(!createContainerDirectories()) {
            return;
        }

        createCommonPomXml();
        createContainerPomXml();

        addModules(Lists.newArrayList(common.getModuleName(), container.getModuleName()));

        copyTemplates();

        updateOrCreateSrc();

    }
}
